#include "oled.h"

static int oled_probe(struct spi_device *spi);
static int oled_remove(struct spi_device *spi);

static int devm_pin_init(struct oled_handle *oled)
{
	int result;

	//获取RST GPIO号
	oled->rst_gpio = of_get_named_gpio(oled->spi->dev.of_node, "rst-gpios", 0);
	if(oled->rst_gpio < 0)
	{
		printk("get rst_gpio failed\r\n");
		return oled->rst_gpio;
	}
	//申请RST GPIO
	result = devm_gpio_request(&oled->spi->dev, oled->rst_gpio, "oled,rst_gpio");
	if(result < 0)
	{
		printk("request rst_gpio failed\r\n");
		return result;
	}
	//设置复位引脚输出高电平
	gpio_direction_output(oled->rst_gpio, 1);

	//获取DC GPIO号
	oled->dc_gpio = of_get_named_gpio(oled->spi->dev.of_node, "dc-gpios", 0);
	if(oled->dc_gpio < 0)
	{
		printk("get dc_gpio failed\r\n");
		return oled->dc_gpio;
	}
	//申请DC GPIO
	result = devm_gpio_request(&oled->spi->dev, oled->dc_gpio, "oled,dc_gpio");
	if(result < 0)
	{
		printk("request dc_gpio failed\r\n");
		return result;
	}
	//设置数据/命令选择引脚输出高电平
	gpio_direction_output(oled->dc_gpio, 1);

	return 0;
}

static void dc_high(struct oled_handle *oled)
{
	gpio_direction_output(oled->dc_gpio, 1);
}

static void dc_low(struct oled_handle *oled)
{
	gpio_direction_output(oled->dc_gpio, 0);
}

static void rst_high(struct oled_handle *oled)
{
	gpio_direction_output(oled->rst_gpio, 1);
}

static void rst_low(struct oled_handle *oled)
{
	gpio_direction_output(oled->rst_gpio, 0);
}

static void oled_reset(struct oled_handle *oled)
{
	//暂时拉高复位引脚
	rst_high(oled);
	msleep_interruptible(200);

	//拉低复位引脚，进行复位
	rst_low(oled);
	msleep_interruptible(200);
	//拉高复位引脚，复位结束
	rst_high(oled);
	msleep_interruptible(200);
}

static int oled_write(struct oled_handle *oled, const uint8_t *data, uint32_t lenght)
{
	int result;
	uint8_t *buffer;
	struct spi_message message;
	struct spi_transfer transfer;

	//分配发送缓存
	buffer = kzalloc(lenght, GFP_KERNEL);
	if(!buffer)
		return -ENOMEM;
	//将数据拷贝到buffer中
	memcpy(buffer, data, lenght);

	//复位spi_transfer
	memset(&transfer, 0, sizeof(transfer));
	//初始化spi_message
	spi_message_init(&message);

	//发送缓存
	transfer.tx_buf = buffer;
	//接收缓存
	transfer.rx_buf = NULL;
	//传输的长度
	transfer.len = lenght;
	//将spi_transfer添加到spi_message队列
	spi_message_add_tail(&transfer, &message);

	//同步传输
	result = spi_sync(oled->spi, &message);

	//释放发送缓存
	kfree(buffer);

	return result;
}

static int oled_write_cmd(struct oled_handle *oled, uint8_t *command, uint32_t lenght)
{
	//拉低数据命令选择引脚，表示发送命令
	dc_low(oled);
	//通过SPI发送数据
	return oled_write(oled, command, lenght);
}

static int oled_write_data(struct oled_handle *oled, uint8_t *data, uint32_t lenght)
{
	//拉高数据命令选择引脚，表示发送数据
	dc_high(oled);
	//通过SPI发送数据
	return oled_write(oled, data, lenght);
}

static int oled_update(struct oled_handle *oled)
{
	int result;
	uint8_t command[3];

	//设置地址
	command[0] = 0x00;					//设置页地址
	command[1] = 0x10;					//设置显示位置—列高地址
	command[2] = 0x00;					//设置显示位置—列低地址
	result = oled_write_cmd(oled, command, 3);
	if(result != 0)
		return result;
	//发送显示数据
	result = oled_write_data(oled, oled->oled_buffer[0], 128*8);
	if(result != 0)
		return result;

	return 0;
}

static int oled_init(struct oled_handle *oled)
{
	int result;
	uint8_t command[28];

	//复位OLED
	oled_reset(oled);

	//发送初始化命令
	command[ 0 ] = 0xAE;				//--turn off oled panel
	command[ 1 ] = 0x00;				//---set low column address
	command[ 2 ] = 0x10;				//---set high column address
	command[ 3 ] = 0x40;				//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	command[ 4 ] = 0x81;				//--set contrast control register
	command[ 5 ] = 0xCF;				 // Set SEG Output Current Brightness
	command[ 6 ] = 0xA1;				//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	command[ 7 ] = 0xC8;				//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	command[ 8 ] = 0xA6;				//--set normal display
	command[ 9 ] = 0xA8;				//--set multiplex ratio(1 to 64)
	command[ 10] = 0x3F;				//--1/64 duty
	command[ 11] = 0xD3;				//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	command[ 12] = 0x00;				//-not offset
	command[ 13] = 0xD5;				//--set display clock divide ratio/oscillator frequency
	command[ 14] = 0x80;				//--set divide ratio, Set Clock as 100 Frames/Sec
	command[ 15] = 0xD9;				//--set pre-charge period
	command[ 16] = 0xF1;				//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	command[ 17] = 0xDA;				//--set com pins hardware configuration
	command[ 18] = 0x12;				
	command[ 19] = 0xDB;				//--set vcomh
	command[ 20] = 0x40;				//Set VCOM Deselect Level
	command[ 21] = 0x20;				//-Set Page Addressing Mode (0x00/0x01/0x02)
	command[ 22] = 0x00;				//
	command[ 23] = 0x8D;				//--set Charge Pump enable/disable
	command[ 24] = 0x14;				//--set(0x10) disable
	command[ 25] = 0xA4;				// Disable Entire Display On (0xa4/0xa5)
	command[ 26] = 0xA6;				// Disable Inverse Display On (0xa6/a7) 
	command[ 27] = 0xAF;				//--turn on oled panel 
	result = oled_write_cmd(oled, command, 28);
	if(result != 0)
		return result;

	//更新OLED显示
	return oled_update(oled);
}

static void convert_fb_to_oled(struct oled_handle *oled)
{
	int i, j, k;

	//一共8*8行，其中每8行1byte
	for(i=0; i<8; i++) {
		//一个128列
		for(j=0; j<16; j++) {
			for(k=0; k<8; k++) {
				oled->oled_buffer[i][j*8+k] = (((oled->fb_buffer[i*8+0][j] >> k) & 0x01) << 0) |
					(((oled->fb_buffer[i*8+1][j] >> k) & 0x01) << 1) |
					(((oled->fb_buffer[i*8+2][j] >> k) & 0x01) << 2) |
					(((oled->fb_buffer[i*8+3][j] >> k) & 0x01) << 3) |
					(((oled->fb_buffer[i*8+4][j] >> k) & 0x01) << 4) |
					(((oled->fb_buffer[i*8+5][j] >> k) & 0x01) << 5) |
					(((oled->fb_buffer[i*8+6][j] >> k) & 0x01) << 6) |
					(((oled->fb_buffer[i*8+7][j] >> k) & 0x01) << 7);
			}
		}
	}
}

static int oled_thread(void *arg)
{
	struct oled_handle *oled;

	oled = (struct oled_handle*)arg;
	while(!kthread_should_stop())
	{
		//缓冲帧内容改变才刷新OLED
		if(memcmp(oled->old_fb_buffer, oled->fb_buffer, OLED_DISPLAY_RAM_SIZE)) {
			//休眠600~700us等待应用层写完
			usleep_range(600, 700);
			//显存格式转换
			convert_fb_to_oled(oled);
			//记录缓冲帧状态
			memcpy(oled->old_fb_buffer, oled->fb_buffer, OLED_DISPLAY_RAM_SIZE);
			//更新显示
			oled_update(oled);
		}
		else {
			//休眠
			msleep_interruptible(2);
		}
	}

	return 0;
}

static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
	chan &= 0xffff;
	chan >>= 16 - bf->length;
	return chan << bf->offset;
}

static int oled_setcolreg(unsigned regno, unsigned red, 
						unsigned green, unsigned blue, 
						unsigned transp, struct fb_info *info)
{
	unsigned int val;
	unsigned int *pseudo_palette;

	if (regno >= 16)
		return -EINVAL;

	val  = chan_to_field(red, &info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue, &info->var.blue);
	pseudo_palette = info->pseudo_palette;
	pseudo_palette[regno] = val;

	return 0;
}

static struct fb_ops oled_ops = {
	.owner = THIS_MODULE,
	.fb_setcolreg = oled_setcolreg,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

static int oled_probe(struct spi_device *spi)
{
	int result;
	struct oled_handle *oled;

	printk("%s\r\n", __FUNCTION__);

	//设置SPI设备的DMA寻址范围，不然dma_alloc会执行失败
	spi->dev.coherent_dma_mask = DMA_BIT_MASK(32);

	//分配OLED句柄
	oled = devm_kmalloc(&spi->dev, sizeof(struct oled_handle), GFP_KERNEL);
	if(!oled)
	{
		printk("alloc oled_buffer failed\r\n");
		return -ENOMEM;
	}
	//复位缓存
	memset(oled, 0x00, sizeof(struct oled_handle));

	//分配缓存
	oled->oled_buffer = devm_kmalloc(&spi->dev, OLED_DISPLAY_RAM_SIZE, GFP_KERNEL);
	if(!oled->oled_buffer)
	{
		printk("alloc oled_buffer failed\r\n");
		return -ENOMEM;
	}
	memset(oled->oled_buffer, 0x00, OLED_DISPLAY_RAM_SIZE);

	//分配存储一次缓冲帧状态的缓存
	oled->old_fb_buffer = devm_kmalloc(&spi->dev, OLED_DISPLAY_RAM_SIZE, GFP_KERNEL);
	if(!oled->old_fb_buffer)
	{
		printk("alloc old_fb_buffer failed\r\n");
		return -ENOMEM;
	}
	memset(oled->old_fb_buffer, 0x00, OLED_DISPLAY_RAM_SIZE);

	/* 分配显存 */
	oled->fb_buffer = dma_alloc_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, &oled->phy_addr, GFP_KERNEL);
	if(!oled->fb_buffer)
	{
		printk("alloc fb_buffer failed\r\n");
		return -ENOMEM;
	}
	memset(oled->fb_buffer, 0x00, OLED_DISPLAY_RAM_SIZE);

	//设置SPI设备的驱动私有数据
	spi->dev.driver_data = (void*)oled;

	//给oled绑定SPI设备
	oled->spi = spi;

	/* 初始化spi_device */
	/*MODE3，CPOL=1，CPHA=1*/
	oled->spi->mode = SPI_MODE_3;
	spi_setup(oled->spi);

	//初始化GPIO
	result = devm_pin_init(oled);
	if(result < 0)
	{
		dma_free_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, oled->fb_buffer, oled->phy_addr);
		printk("init gpio failed\r\n");
		return result;
	}

	//初始化OLED屏幕
	result = oled_init(oled);
	if(result < 0)
	{
		dma_free_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, oled->fb_buffer, oled->phy_addr);
		printk("oled_init failed\r\n");
		return result;
	}

	/* 分配缓冲帧句柄 */
	oled->fb = framebuffer_alloc(0, &spi->dev);
	if(!oled->fb)
	{
		dma_free_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, oled->fb_buffer, oled->phy_addr);
		printk("alloc fb failed\r\n");
		return -ENOMEM;
	}

	/* 设置fb */
	/* 显存虚拟地址和大小 */
	oled->fb->screen_base = (char*)oled->fb_buffer;
	oled->fb->screen_size = OLED_DISPLAY_RAM_SIZE;
	/* LCD分辨率、颜色格式 */
	oled->fb->var.xres = 128;
	oled->fb->var.yres = 64;
	oled->fb->var.xres_virtual = 128;
	oled->fb->var.yres_virtual = 64;
	oled->fb->var.bits_per_pixel = 1;
	/* ID */
	strcpy(oled->fb->fix.id, "atk,oled");
	/* 显存大小和物理地址 */
	oled->fb->fix.smem_len = OLED_DISPLAY_RAM_SIZE;
	oled->fb->fix.smem_start = oled->phy_addr;
	/* 一行的显存长度 */
	oled->fb->fix.line_length = 16;
	/* 显示器类型 */
	oled->fb->fix.type = FB_TYPE_PACKED_PIXELS;
	/* 像素格式 */
	oled->fb->fix.visual = FB_VISUAL_MONO10;
	/* 底层操作函数集合 */
	oled->fb->fbops = &oled_ops;
	/* 颜色表 */
	oled->fb->pseudo_palette = oled->pseudo_palette;

	/* 注册缓冲帧驱动 */
	result = register_framebuffer(oled->fb);
	if(result < 0)
	{
		framebuffer_release(oled->fb);
		dma_free_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, oled->fb_buffer, oled->phy_addr);
		printk("register fb failed\r\n");
		return result;
	}

	/* 创建内核线程，更新OLED */
	oled->kthread = kthread_create(oled_thread, (void*)oled, "oled_thread%d,%d", oled->spi->controller->bus_num, oled->spi->chip_select);
	if(IS_ERR(oled->kthread))
	{
		unregister_framebuffer(oled->fb);
		framebuffer_release(oled->fb);
		dma_free_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, oled->fb_buffer, oled->phy_addr);
		printk("create oled_thread failed\r\n");
		return PTR_ERR(oled->kthread);
	}
	wake_up_process(oled->kthread);

	return 0;
}

//设备或驱动卸载时执行
static int oled_remove(struct spi_device *spi)
{
	struct oled_handle *oled;

	printk("%s\r\n", __FUNCTION__);

	oled = (struct oled_handle*)spi->dev.driver_data;
	if(!oled)
	{
		printk("verification failed\r\n");
		return -EINVAL;
	}

	kthread_stop(oled->kthread);
	unregister_framebuffer(oled->fb);
	framebuffer_release(oled->fb);
	dma_free_wc(&spi->dev, OLED_DISPLAY_RAM_SIZE, oled->fb_buffer, oled->phy_addr);

	return 0;
}

/* 匹配列表，用于设备树和平台驱动匹配 */
static const struct of_device_id oled_of_match[] = {
	{.compatible = "atk,oled"},
	{ /* Sentinel */ }
};
/* 传统匹配方式ID列表 */
static const struct spi_device_id oled_id[] = {
	{}
};
//SPI驱动
static struct spi_driver oled_drv = {
	.driver = {
		.name = "oled",
		.owner = THIS_MODULE,
		.pm = NULL,
		.of_match_table = oled_of_match,
	},
	.id_table = oled_id,
	.probe = oled_probe,
	.remove = oled_remove,
};
static int __init oled_drv_init(void)
{
	int result = 0;

	printk("%s\r\n", __FUNCTION__);

	//注册SPI驱动
	result = spi_register_driver(&oled_drv);
	if(result < 0)
	{
		printk("add cdev failed\r\n");
		return result;
	}

	return 0;
}

static void __exit oled_drv_exit(void)
{
	printk("%s\r\n", __FUNCTION__);

	//注销SPI驱动
	spi_unregister_driver(&oled_drv);
}

module_init(oled_drv_init);
module_exit(oled_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LF");
MODULE_DESCRIPTION("oled_dev");

